package com.hlkj.pay.app.merchant.ext.impl.gtzfpay.utils;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;

import javax.crypto.Cipher;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

/**
 * RSA加解密工具类
 *
 * @Date: 2021/10/14 20:39
 * @Author: pandans
 */
public class GtzfPayRsaUtil {
    private static final String ALGORITHM = "RSA";
    /**
     * 加解密算法/工作模式/填充方式
     */
    private static final String ALGORITHM_SHA256 = "SHA-256";

    public static String getContent(JSONObject obj) {
        Set<String> set = obj.keySet();
        TreeSet<String> keys = new TreeSet<>(set);
        StringBuilder sb = new StringBuilder();
        for (String k : keys) {
            sb.append(k).append("=").append(obj.get(k)).append("&");
        }
        return sb.substring(0, sb.length() - 1);
    }

    public static String encrypt(JSONObject obj, String publicKey) {
        String content = getContent(obj);
        return encrypt(content, publicKey);
    }

    /**
     * @param content:   待加密内容
     * @param publicKey: 加密公钥
     * @return 加密过的内容
     * @Description 使用公钥进行你内容假面
     * @date: 2021/10/17 11:22
     * @author: pandans
     */
    public static String encrypt(String content, String publicKey) {
        if (publicKey == null || "".equals(publicKey)) {
            //缺少Rsa密钥信息
            return null;
        }
        try {
            String hexHash = getSHA256(content);
            PublicKey pubKey = getPublicKey(publicKey);
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            byte[] bytes = hexHash.getBytes();
            ByteArrayInputStream read = new ByteArrayInputStream(bytes);
            ByteArrayOutputStream write = new ByteArrayOutputStream();
            byte[] buf = new byte[117];
            int len = 0;
            while ((len = read.read(buf)) != -1) {
                byte[] buf1 = null;
                if (buf.length == len) {
                    buf1 = buf;
                } else {
                    buf1 = new byte[len];
                    for (int i = 0; i < len; i++) {
                        buf1[i] = buf[i];
                    }
                }
                byte[] bytes1 = cipher.doFinal(buf1);
                write.write(bytes1);
            }

            return GtzfPayBase64.encode2String(write.toByteArray());
        } catch (Exception e) {
            //签名失败
            return null;
        }
    }

    private static String getSHA256(String content) throws NoSuchAlgorithmException {
        // 创建SHA-256摘要实例
        MessageDigest md = MessageDigest.getInstance(ALGORITHM_SHA256);
        // 将字符串转换为UTF-8字节数组
        byte[] hashBytes = md.digest(content.getBytes(StandardCharsets.UTF_8));
        // 将字节数组转换为十六进制字符串
        return bytesToHex(hashBytes);
    }

    // 解密函数
    public static String decrypt(String encryptedContent, String privateKey) {
        if (privateKey == null || "".equals(privateKey)) {
            // 缺少私钥信息
            return null;
        }
        try {
            byte[] encryptedBytes = GtzfPayBase64.decode2Byte(encryptedContent);
            PublicKey publicKey = GtzfPayRsaUtil.getPublicKey(privateKey);
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, publicKey);
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            int blockSize = 256;
            for (int i = 0; i < encryptedBytes.length; i += blockSize) {
                byte[] chunk = Arrays.copyOfRange(encryptedBytes, i, Math.min(i + blockSize, encryptedBytes.length));
                outputStream.write(cipher.doFinal(chunk));
            }
            return new String(outputStream.toByteArray(), StandardCharsets.UTF_8);
        } catch (Exception e) {
            e.printStackTrace();
            // 解密失败
            return null;
        }
    }

    public static boolean verify(String plainText, String publicKey) {
        if (publicKey == null || "".equals(publicKey)) {
            // 缺少私钥信息
            return false;
        }
        try {

            TreeMap<String, String> map = JSONUtil.toBean(plainText, TreeMap.class);
            StringBuilder sb = new StringBuilder();
            String sign = "";
            for (String key : map.keySet()){
                if("sign".equals(key)){
                    sign = map.get(key);
                    continue;
                }
                sb.append(key).append("=").append(map.get(key)).append("&");
            }
            String res = sb.substring(0,sb.lastIndexOf("&"));
            String sha256 = getSHA256(res);
            String decodeSha256 = decrypt(sign, publicKey);
            return sha256.equals(decodeSha256);
        } catch (Exception ex) {
            return false;
        }
    }

    // 将字节数组转换为十六进制字符串
    private static String bytesToHex(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            String hex = String.format("%02x", b & 0xFF);
            hexString.append(hex);
        }
        return hexString.toString();
    }

    public static RSAPublicKey getPublicKey(String publicKey) throws Exception {
        byte[] keyBytes = GtzfPayBase64.decode2Byte(publicKey);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        return (RSAPublicKey) keyFactory.generatePublic(spec);
    }
}
